package network;
import filter.RulesChecker;
import gui.MessageMonitor;

import java.io.*;
import java.net.*;
import java.util.Calendar;
import java.util.Vector;

import log.Logger;

public class NetController extends Thread {
	private static boolean DEBUG = true;
	private static final int MAXC_PERTIME = 5000;
	private static final int MAX_WAIT_TIME = 3000;
	int myNumber = -1;
	MessageMonitor mainFrame;
	InetAddress targetIP;
	int targetPort;
	Communicator comm;
	Socket socket;
	InputStream socketIS;
	String res = "";
	boolean isEnd = false, mainClient = false;
	Logger logger;
	RulesChecker rulesChecker = new RulesChecker();
	
	public NetController(MessageMonitor newJFrame) {
		mainFrame = newJFrame;
		logger = new Logger("log.txt");
	}
	
	public NetController(MessageMonitor newJFrame, String ip, String port, int num){
		mainFrame = newJFrame;
		logger = new Logger("log_" + ip + "_" + port + ".txt");
		myNumber = num;
	}

	public void setIP(String ipStr) {
		try {
			targetIP = InetAddress.getByName(ipStr);
		} catch (UnknownHostException e) {
			println("bad address.");
			e.printStackTrace();
		}
	}
	
	public void setLog(String s) {
		logger.setMaxSize(Integer.parseInt(s));
	}
	
	public void setPort(String portStr) {
		try {
			targetPort = Integer.parseInt(portStr);
		} catch (Exception e) {
			println("bad port.");
			e.printStackTrace();
		}
	}

	public void setFilter(String text) {
		Object[] ret = rulesChecker.make_it_correct(text);
		if ((Boolean)ret[0])
			rulesChecker.set(text);
		else
			mainFrame.updateFilter((String)ret[1]);
	}

	private void commit() {
		comm.notifyOthers(res);
//		println("commit string: " + res);
//		println("commit number: " + myNumber);
		Object[] ret = rulesChecker.check(res);
		
		if (!(Boolean)ret[0]) {
			res = "";
			return;
		}
		
		boolean[] redMark = (boolean[]) ret[1];
		Vector<Integer> vec = new Vector<Integer>();
		for (int i=0; i<redMark.length; ++i)
			if (redMark[i]) {
				vec.add(i);
				for(;i<redMark.length;++i)
					if (i+1==redMark.length || !redMark[i+1]) {
						vec.add(i+1);
						break;
					}
			}
		
		res = getTime() + " " + res;
//		println(res);
		logger.writeLog(res);
		int[] arr = new int[vec.size()];
		for (int i=0; i<arr.length; ++i)
			arr[i] = vec.elementAt(i);
		mainFrame.set(myNumber, res, null);
		res = "";
	}
	
	private static String getTime() {
		Calendar c = Calendar.getInstance();
		String ret = "";
		ret += "[" + c.get(Calendar.YEAR);
		ret += "/" + (c.get(Calendar.MONTH)+1);
		ret += "/" + c.get(Calendar.DAY_OF_MONTH);
		ret += " " + c.get(Calendar.HOUR_OF_DAY);
		ret += ":" + c.get(Calendar.MINUTE);
		ret += ":" + c.get(Calendar.SECOND) + "]";
		return ret;
	}

	private static void println(Object x) {
		if (DEBUG == false) return;
		System.out.println(x);
	}
		
	public void run() {
		while (true) {
			try {
				InetSocketAddress endpoint = new InetSocketAddress(targetIP, targetPort);
				socket = new Socket();
				socket.connect(endpoint, 1000);
				mainClient = true;
				startServer();
				startRead();
				comm.closeBroadcastListener();
				break;
				//socket.close(); closed in startRead()
			} catch (Exception e) {
				if (e instanceof SocketTimeoutException || e instanceof ConnectException) {
					if (findServer())
						startRead();
					if (isEnd) break;
				}
				else
					e.printStackTrace();
			}
		}
	}
	
	private void startServer() {
		comm = new Communicator(targetIP, targetPort);
		comm.startListen();
	}
	
	private boolean findServer() {
		comm = new Communicator(targetIP, targetPort);
		socket = comm.findServer();
		if (socket == null) return false;
		return true;
	}

	private void startRead() {
		try {
			socketIS = socket.getInputStream();
			byte[] byteval = new byte[MAXC_PERTIME];
			while (!isEnd) {
				if (mainClient) socket.setSoTimeout(0);
				else socket.setSoTimeout(MAX_WAIT_TIME);
				int len = socketIS.read(byteval);
//				println("read data." + " length = " + len);
				if (len != -1) {
					res += new String(byteval, 0, len);
					if (res.length() > 0 && len < MAXC_PERTIME) commit();
				} else {
					//connect closed
					if (res.length() > 0) commit();
					println("no more data.");
					break;
				}
			}
			if (res.length() > 0) commit();
			socketIS.close();
			socket.close();
		} catch (SocketTimeoutException e) {
			try {
				socketIS.close();
				socket.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		} catch (IOException e) {
			e.printStackTrace();
			try {
				socketIS.close();
				socket.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}

	public void close() {
		isEnd = true;
	}
}
